﻿/*********************************************************************************
**                                                                              **
**  Copyright (C) 2022-2022 LiLong                                              **
**  This file is part of BluetoothCpp.                                          **
**                                                                              **
**  BluetoothCpp is free software: you can redistribute it and/or modify        **
**  it under the terms of the GNU Lesser General Public License as published by **
**  the Free Software Foundation, either version 3 of the License, or           **
**  (at your option) any later version.                                         **
**                                                                              **
**  BluetoothCpp is distributed in the hope that it will be useful,             **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of              **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               **
**  GNU Lesser General Public License for more details.                         **
**                                                                              **
**  You should have received a copy of the GNU Lesser General Public License    **
**  along with BluetoothCpp.  If not, see <https://www.gnu.org/licenses/>.      **
**********************************************************************************/
#include "Uuid.h"

#include <format>
#include <ranges>
#include <vector>

namespace BTH
{
template<typename T>
inline void uuidStringHelper(std::string_view uuid, T& value, int& offset)
{
    std::from_chars(uuid.data() + offset, uuid.data() + offset + sizeof(T) * 2, value, 16);
    offset += sizeof(T) * 2;
}

struct Uuid::Impl
{
    // bluetooth base uuid 00000000-0000-1000-8000-00805F9B34FB
    unsigned long data1 { 0x0000'0000 };
    unsigned short data2 { 0x0000 };
    unsigned short data3 { 0x1000 };
    unsigned char data4[8] { 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
};

Uuid::Uuid() noexcept : m_impl(std::make_unique<Impl>()) {}

Uuid::Uuid(unsigned short uuid) noexcept : Uuid() { m_impl->data1 = uuid; }

Uuid::Uuid(unsigned int uuid) noexcept : Uuid() { m_impl->data1 = uuid; }

Uuid::Uuid(const Uuid& other) noexcept : Uuid() { operator=(other); }

Uuid::Uuid(Uuid&& other) noexcept { std::swap(m_impl, other.m_impl); }

Uuid::Uuid(std::string_view uuid) : Uuid()
{
    const bool hasBrace = uuid.starts_with("{");
    if (uuid.size() != (hasBrace ? 38 : 36))
        throw std::runtime_error("Invalid uuid string");
    int offset = (hasBrace ? 1 : 0);
    try
    {
        uuidStringHelper(uuid, m_impl->data1, offset);
        uuidStringHelper(uuid, m_impl->data2, ++offset);
        uuidStringHelper(uuid, m_impl->data3, ++offset);
        uuidStringHelper(uuid, m_impl->data4[0], ++offset);
        uuidStringHelper(uuid, m_impl->data4[1], offset);
        offset++;
        for (int i = 0; i < 6; i++)
        {
            uuidStringHelper(uuid, m_impl->data4[i + 2], offset);
        }
    }
    catch (...)
    {
        throw std::runtime_error("Invalid uuid string");
    }
}

Uuid::Uuid(const GUID& guid) noexcept : Uuid()
{
    m_impl->data1 = guid.Data1;
    m_impl->data2 = guid.Data2;
    m_impl->data3 = guid.Data3;
    memcpy(m_impl->data4, guid.Data4, sizeof(guid.Data4));
}

Uuid::~Uuid() noexcept {}

std::string Uuid::toString() const noexcept
{
    return std::format("{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
                       m_impl->data1,
                       m_impl->data2,
                       m_impl->data3,
                       m_impl->data4[0],
                       m_impl->data4[1],
                       m_impl->data4[2],
                       m_impl->data4[3],
                       m_impl->data4[4],
                       m_impl->data4[5],
                       m_impl->data4[6],
                       m_impl->data4[7]);
}

#ifdef WIN32
Uuid::operator GUID() const noexcept
{
    return GUID { m_impl->data1,
                  m_impl->data2,
                  m_impl->data3,
                  { m_impl->data4[0],
                    m_impl->data4[1],
                    m_impl->data4[2],
                    m_impl->data4[3],
                    m_impl->data4[4],
                    m_impl->data4[5],
                    m_impl->data4[6],
                    m_impl->data4[7] } };
}
#endif

Uuid& Uuid::operator=(Uuid&& other) noexcept
{
    std::swap(m_impl, other.m_impl);
    return *this;
}

Uuid& Uuid::operator=(const Uuid& other) noexcept
{
    m_impl->data1 = other.m_impl->data1;
    m_impl->data2 = other.m_impl->data2;
    m_impl->data3 = other.m_impl->data3;
    std::copy(other.m_impl->data4, other.m_impl->data4 + sizeof(other.m_impl->data4), m_impl->data4);
    return *this;
}

} // namespace BTH
